﻿using System;
using System.Xml;
using System.ComponentModel;
using System.Windows.Forms;
using SdmxMl.Common;

namespace SdmxMl.Structure
{
    /// <summary>
    /// A metadata structure definition performs several functions: it groups sets
    /// of objects into "targets" against which reference metadata may be reported.
    /// Targets define the structure of the reference metadata "keys" which
    /// identify specific types of reported metadata, and describe the valid
    /// values for populating the keys. Also, metadata structure definitions
    /// provide a presentational organization of concepts for reporting purposes.
    /// The structure of a reference metadata report is derived from this
    /// presentational structure. Also, representations - unless defaults from
    /// the concepts are used - must be indicated as part of this presentational
    /// structure. Attributes allow the assignment of an ID, an agency, a version,
    /// and a uri. It is possible to use the uri field to point to the location of
    /// an SDMX-ML Structure message which contains a more detailed version of the
    /// metadata structure definition. (This is termed an "external reference".)
    /// If an external reference is being made, the isExternalReference attribute
    /// must be set to "true". When an external reference is being made, none of
    /// the child elements should be included. Otherwise, both TargetIdentifiers
    /// and at least one ReportStructure must be included. The urn attribute holds
    /// a valid SDMX registry URN (see the SDMX Registry Specification).
    /// The validFrom and validTo attributes provide inclusive dates for providing
    /// supplemental validity information about the version
    /// </summary>
    public class MetadataStructureDefinitionType : Artefact
    {
        #region Properties

        public override ArtefactRefTypedList ArtefactRefList
        {
            get
            {
                ArtefactRefTypedList list = new ArtefactRefTypedList();

                // No more as lock references (was commented out)
                 
                //Target Identifier, Identifier components, Representation Scheme
                IdentifierComponentTypeList idcList = TargetIdentifiers.FullTargetIdentifier.IdentifierComponentList;
                foreach (IdentifierComponentType idc in idcList)
                {
                    RepresentationSchemeType rps = idc.RepresentationScheme;
                    if (rps.SchemeType != RepresentationSchemeTypeType.External)
                    {
                        ArtefactRefTyped at = null;
                        switch (rps.SchemeType)
                        {
                                /* loop so do not use
                            case RepresentationSchemeTypeType.Category:
                                at = new ArtefactRefTyped(
                                    SdmxMl.Manager.SdmxArtefactType.CategorySchemes,
                                    rps.Id, rps.AgencyId, rps.Version);
                                break;
                            */
                            case RepresentationSchemeTypeType.Codelist:
                                at = new ArtefactRefTyped(
                                    SdmxMl.Manager.SdmxArtefactType.CodeLists,
                                    rps.Id, rps.AgencyId, rps.Version);
                                break;

                            case RepresentationSchemeTypeType.Concept:
                                at = new ArtefactRefTyped(
                                    SdmxMl.Manager.SdmxArtefactType.Concepts,
                                    rps.Id, rps.AgencyId, rps.Version);
                                break;
                        }

                        if (at != null && at.IsFilled)
                            if (list.Contains(at.Type, at.FullIdent) == false)
                                list.Add(at);
                    }
                }
                
                // Report(s)
                foreach (ReportStructureType rst in ReportStructureList)
                    // Recurse
                    FillReportRefList(rst.MetadataAttributeList, list);
                
                // Now Concept in referenced conceptSchemes can also make references
                // But we do not assume this responsability

                return list;
            }
        }

        private void FillReportRefList(MetadataAttributeTypeList metaAttList, ArtefactRefTypedList list)
        {
            foreach (MetadataAttributeType md in metaAttList)
            {
                // ConceptScheme Ref
                ArtefactRefTyped at = new ArtefactRefTyped(
                    SdmxMl.Manager.SdmxArtefactType.Concepts,
                    md.ConceptSchemeInfo.Id, md.ConceptSchemeInfo.AgencyId, md.ConceptSchemeInfo.Version);
                if (at.IsFilled && list.Contains(at.Type, at.FullIdent) == false)
                    list.Add(at);

                // Represaentation only defined for Codelist
                at = new ArtefactRefTyped(
                    SdmxMl.Manager.SdmxArtefactType.CodeLists,
                    md.RepresentationInfo.Id, md.RepresentationInfo.AgencyId, md.RepresentationInfo.Version);
                if (at.IsFilled && list.Contains(at.Type, at.FullIdent) == false)
                    list.Add(at);

                // Lower level (if any)
                if (md.MetadataAttributeList != null)
                    FillReportRefList(md.MetadataAttributeList, list);

            }
        }

        // SDMX 2.0 alloys only one target
        private TargetIdentifiersType _targetIdentifiers;
        public TargetIdentifiersType TargetIdentifiers
        {
            get { return _targetIdentifiersList[0]; }
        }
        // SDMX 2.1 allows multiple metadata targets
        private TargetIdentifiersTypeList _targetIdentifiersList;
        public TargetIdentifiersTypeList TargetIdentifiersList
        {
            get { return _targetIdentifiersList; }
        }

        private ReportStructureTypeList _reportStructureList;
        public ReportStructureTypeList ReportStructureList
        {
            get { return _reportStructureList; }
        }

        #endregion //Properties

        #region Constructors

        public MetadataStructureDefinitionType()
            : base()
        {
            _targetIdentifiersList = new TargetIdentifiersTypeList();
            _targetIdentifiers = new TargetIdentifiersType();
            _targetIdentifiersList.Add(_targetIdentifiers);
            _reportStructureList = new ReportStructureTypeList(this);
        }


        public MetadataStructureDefinitionType(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr) 
            : base(nodeArtefact, nsmgr)
        {
            string query = string.Empty;
            if (nsmgr.VersionSdmx == SdmxVersion.SDMX_20)
            {
                _reportStructureList = new ReportStructureTypeList(this);
                query = "./structure:ReportStructure";
                XmlNodeList nodeList = nodeArtefact.SelectNodes(query, nsmgr);
                if (nodeList.Count > 0)
                {
                    foreach (XmlNode nd in nodeList)
                    {
                        ReportStructureType rpt = new ReportStructureType(this, nd, nsmgr);
                        _reportStructureList.Add(rpt);
                    }
                }

                query = "./structure:TargetIdentifiers";
                XmlNode node = nodeArtefact.SelectSingleNode(query, nsmgr);
                if (node != null)
                {
                    _targetIdentifiersList = new TargetIdentifiersTypeList();
                    _targetIdentifiers = new TargetIdentifiersType(node, nsmgr);
                    _targetIdentifiersList.Add(_targetIdentifiers);
                }
                else
                    throw new ApplicationException("Unexpected, No targetIdentifiers in Metadatastructure " + Id);
            }
            else // 2.1
            {
                // Metadata Targets
                _targetIdentifiersList = new TargetIdentifiersTypeList();
                query = "./structure:MetadataStructureComponents/structure:MetadataTarget";
                XmlNodeList nodeList = nodeArtefact.SelectNodes(query, nsmgr);
                if (nodeList.Count > 0)
                {
                    foreach (XmlNode nd in nodeList)
                    {
                        _targetIdentifiers = new TargetIdentifiersType(nd, nsmgr);
                        _targetIdentifiersList.Add(_targetIdentifiers);
                    }
                }
                else
                {
                    _targetIdentifiers = new TargetIdentifiersType();
                    _targetIdentifiersList.Add(_targetIdentifiers);
                }

                // Report Structures
                _reportStructureList = new ReportStructureTypeList(this);
                query = "./structure:MetadataStructureComponents/structure:ReportStructure";
                nodeList = nodeArtefact.SelectNodes(query, nsmgr);
                if (nodeList.Count > 0)
                {
                    foreach (XmlNode nd in nodeList)
                    {
                        ReportStructureType rpt = new ReportStructureType(this, nd, nsmgr);
                        _reportStructureList.Add(rpt);
                    }
                }

            }
        }

        #endregion //Constructors

        #region XML serialize

        public override void WriteXmlInsider(XmlTextWriter xw)
        {
            string prefix = "structure";

            if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_20)
            {
                // Required
                _targetIdentifiers.WriteXml(xw, prefix, "TargetIdentifiers");

                if (_reportStructureList.Count > 0)
                    foreach (ReportStructureType rpt in _reportStructureList)
                        rpt.WriteXml(xw, prefix, "ReportStructure");
            }
            else
            {
                xw.WriteStartElement(prefix, "MetadataStructureComponents", null);

                //structure:MetadataTarget id
                xw.WriteStartElement(prefix, "MetadataTarget", null);
                string targetId = _targetIdentifiers.FullTargetIdentifier.Id;
                xw.WriteAttributeString("id", targetId);
               
                // Each fullTargetIdentifier is a metadata target with an identifiable object target
                foreach (IdentifierComponentType it in _targetIdentifiers.FullTargetIdentifier.IdentifierComponentList)
                {
                    //structure:IdentifiableObjectTarget objecttype
                    it.WriteXml(xw, prefix, "IdentifiableObjectTarget");
                }
                xw.WriteEndElement();

                foreach (ReportStructureType rpt in _reportStructureList)
                    rpt.WriteXml(xw, prefix, "ReportStructure");


                xw.WriteEndElement();
            }
        }
            
        

        #endregion

        #region Tree

        public override TreeNode FillNode(TreeNode parentNode)
        {
            TreeNode t = base.FillNode(parentNode);

            TreeNode ndMetaTargets = new TreeNode(_targetIdentifiersList.ToString(), 1, 1);
            ndMetaTargets.Tag = _targetIdentifiersList;
            t.Nodes.Add(ndMetaTargets);
            foreach (TargetIdentifiersType ti in _targetIdentifiersList)
                ti.FillNode(ndMetaTargets);

            TreeNode ndReport = new TreeNode(_reportStructureList.ToString(), 1, 1);
            ndReport.Tag = _reportStructureList;
            t.Nodes.Add(ndReport);
            foreach (ReportStructureType rpt in _reportStructureList)
                rpt.FillNode(ndReport);

            return t;


        }

        #endregion //Tree

        #region Artefact Type

        public override Manager.SdmxArtefactType ArType
        {
            get { return Manager.SdmxArtefactType.MetadataStructureDefinitions; }
        }

        #endregion

    }
}
